/* Copyright (C) 2021 Rain */

/* This file is part of XNIX. */

/* 
  XNIX is free software: you can redistribute it and/or modify 
  it under the terms of the GNU General Public License as published by 
  the Free Software Foundation, either version 3 of the License, or 
  (at your option) and later version. 
*/

/*
  XNIX is distributed in the hope that it will be useful, 
  but WITHOUT ANY WARRANTY; without even the implied warranty of 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
  GNU General Public License for more details. 
*/

/*
  You should have received a copy of the GNU General Public License 
   along with XNIX. If not, see <https://www.gnu.org/licenses/>.  
*/




#include <kernel/init.h>
#include <kernel/errno.h>
#include <kernel/types.h>

#include <kernel/int.h>
#include <kernel/pci.h>
#include <kernel/pci_vendors.h>

#include <lib/string.h>

#include <arch/x86/int.h>
#include <arch/io.h>
#include <arch/sys.h>



__uint32_t pci_read_config(__uint8_t bus, __uint8_t slot, __uint8_t func, __uint8_t offset) {
	__uint32_t addr;

#define lbus  ((__uint32_t) bus)
#define lslot ((__uint32_t) slot)
#define lfunc ((__uint32_t) func)

	addr = (lbus << 16) | (lslot << 11) | (lfunc << 8) | (offset & 0xfc) | 0x80000000ul;

	outl(addr, 0xcf8);

	return inl(0xcfc);
}

__uint32_t pci_lspci(void) {
	__uint8_t bus, slot, func;
	__uint32_t devs = 0;

	for (bus = 0; bus < 255; bus++) {
		for (slot = 0; slot < 32; slot++) {
			for (func = 0; func < 8; func++) {
				if (pci_read_config(bus, slot, func, 0) != 0xffffffff) {
					/* exists */
					printk(yellow, "pci: found %b:%b:%b %w %x. \n", 
						bus, slot, func, 
						pci_read_config(bus, slot, func, 8) >> 16, 
						pci_read_config(bus, slot, func, 0));
					devs++;

					pci_infodev(bus, slot, func);
				}
			}
		}
	}

	return devs;
}


void pci_infodev(__uint8_t bus, __uint8_t slot, __uint8_t func) {
	__uint32_t buf[256];
	struct pci_header header;
	int i;

	for (i = 0; i < 256 / 4; i++) 
		buf[i] = pci_read_config(bus, slot, func, i << 2);

	memcpy((char *) &header, (char *) buf, sizeof(struct pci_header));

	/* put the info of this device on the screen */
/*	printk(yellow, "vendor id: %w\n", header.vendor);
	printk(yellow, "device id: %w\n", header.dev);

	printk(yellow, "revision: %b\n", header.revision);
	printk(yellow, "sub class id: %b\n", header.subclass);
	printk(yellow, "class code: %b\n", header.class_code);

	printk(yellow, "cache line size: %b\n", header.cache_size);
	printk(yellow, "latency: %b\n", header.latency);

	printk(yellow, "header type: %b\n", header.hdr_type);
*/

	struct pci_header0 *hdr = (struct pci_header0 *) buf;

/*		printk(yellow, "bar0: %x  ", hdr->bar0);
		printk(yellow, "bar1: %x\n", hdr->bar1);

		printk(yellow, "bar2: %x  ", hdr->bar2);
		printk(yellow, "bar3: %x\n", hdr->bar3);

		printk(yellow, "bar4: %x  ", hdr->bar4);
		printk(yellow, "bar5: %x\n", hdr->bar5);

		printk(yellow, "irq: %b\n", hdr->irqnr);
*/

	/* search in the vendor-dev table, and got the dev_init */
	
	
	errno_t (* dev_init) (struct pci_header0 *) = NULL;

	for (i = 0; ; i++) {
		if (pci_vendors[i].class == 0xff) 
			break;

		if (pci_vendors[i].class == header.class_code && 
			pci_vendors[i].subclass == header.subclass) {
			dev_init = pci_vendors[i].dev_init;
			break;
		}
	}

	if (!dev_init) {
		printk(red, "module not found for vendor %w, device %w. \n", 
				header.vendor, header.dev);
		return;
	}

	printk(yellow, "dev_init: %q\n", dev_init);

	dev_init(hdr);

	return;
}

